//---------------------------------------------------------------------------------
// Copyright (c) 2011, Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//---------------------------------------------------------------------------------

namespace Microsoft.Samples.ServiceBus.WindowsPhoneChat.Controllers
{
    using System;
    using System.Net;
    using Microsoft.Samples.ServiceBus;
    using Microsoft.Samples.ServiceBus.Messaging;
    using Microsoft.Samples.ServiceBus.WindowsPhoneChat.ViewModels;

    public class ChatMessageReceiver
    {
        private readonly ChatTopicViewModel topicViewModel;
        private bool isEnabled;

        public ChatMessageReceiver(ChatTopicViewModel topicViewModel)
        {
            if (topicViewModel == null)
            {
                throw new ArgumentNullException("topicViewModel");
            }

            this.topicViewModel = topicViewModel;
            this.topicViewModel.SubscriptionChanged += this.OnSubscriptionChanged;
        }

        public event MessageReceivedEventHandler MessageReceived;

        public ChatTopicViewModel TopicViewModel
        {
            get { return this.topicViewModel; }
        }

        public bool IsEnabled
        {
            get
            {
                return this.isEnabled;
            }

            set
            {
                if (this.isEnabled != value)
                {
                    this.isEnabled = value;
                    this.OnIsEnabledChanged();
                }
            }
        }

        protected bool OnMessageReceived(ChatMessageViewModel message)
        {
            MessageReceivedEventArgs eventArgs = null;

            if (this.MessageReceived != null)
            {
                eventArgs = new MessageReceivedEventArgs(message);
                this.MessageReceived(this, eventArgs);
            }

            return eventArgs != null && !eventArgs.Abandon;
        }

        private void OnSubscriptionChanged(object sender, EventArgs e)
        {
            this.IsEnabled = this.TopicViewModel.SubscriptionClient != null;
        }

        private void OnIsEnabledChanged()
        {
            if (this.IsEnabled && this.TopicViewModel.SubscriptionClient != null)
            {
                var subscriptionClient = this.TopicViewModel.SubscriptionClient;
                subscriptionClient.BeginReceive(this.OnReceiveCompleted, subscriptionClient);
            }
        }

        private void OnReceiveCompleted(IAsyncResult result)
        {
            var subscriptionClient = (SubscriptionClient)result.AsyncState;
            try
            {
                var message = subscriptionClient.EndReceive(result);

                // if a message is received and this is the current topic, process 
                // the message. Otherwise, Abandon the message to return it to the 
                // subscription.
                if (this.IsEnabled)
                {
                    // if a message was received, display it in the UI and then
                    // complete it.
                    if (message != null)
                    {
                        ViewController.Current.BeginInvokeUI(() => this.DispatchMessage(message, subscriptionClient));
                    }

                    // if we're still the current topic and have an active receiver
                    // try to receive the next message from the subscription
                    if (this.IsEnabled)
                    {
                        subscriptionClient.BeginReceive(this.OnReceiveCompleted, subscriptionClient);
                    }
                }
                else
                {
                    // if the receive completed after this topic ceased to be the 
                    // current topic (i.e. IsCurrentTopic = false) abandon any
                    // received message
                    if (message != null)
                    {
                        subscriptionClient.BeginAbandon(message, this.OnAbandonMessageCompleted, subscriptionClient);
                    }
                }
            }
            catch (HttpWebException w)
            {
                // If the topic (or subscription) is deleted when a receive is in
                // progress, then a 404 (NotFound) exception is thrown at EndReceive
                if (w.StatusCode == HttpStatusCode.NotFound)
                {
                    // The topic does not exist, it may have been deleted
                    // so remove it from the UI
                    this.TopicViewModel.SubscriptionClient = null;
                    this.IsEnabled = false;
                }
            }
            catch (Exception e)
            {
                App.ShowError(e);
            }
        }

        private void DispatchMessage(BrokeredMessage message, SubscriptionClient subscriptionClient)
        {
            ChatMessageViewModel chatMessage;
            if (ChatMessageViewModel.TryCreate(message, out chatMessage))
            {
                var success = true;
                try
                {
                    success = this.OnMessageReceived(chatMessage);
                }
                finally
                {
                    if (success)
                    {
                        subscriptionClient.BeginComplete(message, this.OnCompleteMessageCompleted, subscriptionClient);
                    }
                    else
                    {
                        subscriptionClient.BeginAbandon(message, this.OnCompleteMessageCompleted, subscriptionClient);
                    }
                }
            }
        }

        private void OnCompleteMessageCompleted(IAsyncResult result)
        {
            var subscriptionClient = (SubscriptionClient)result.AsyncState;

            try
            {
                subscriptionClient.EndComplete(result);
            }
            catch (Exception e)
            {
                App.ShowError(e);
            }
        }

        private void OnAbandonMessageCompleted(IAsyncResult result)
        {
            var subscriptionClient = (SubscriptionClient)result.AsyncState;

            try
            {
                subscriptionClient.EndAbandon(result);
            }
            catch (Exception e)
            {
                App.ShowError(e);
            }
        }
    }
}
